Repository navigation
zircon: fix checksum end-around carry folding in IP TX deparse - #38
Open
john-kearney wants to merge 1 commit into
Open
john-kearney wants to merge 1 commit into
john-kearney wants to merge 1 commit into
Conversation
The TX checksum accumulators were folded a single time before truncation to 16 bits. When the sum is large enough for a carry to survive one end-around fold (reachable with maximal field values on jumbo or edge-value traffic), the emitted TCP/UDP/IPv4 checksum is off by one from the correct value. Fold a second time before the final truncation so that no carry can survive: for any 21-bit accumulator value, folding twice always yields a value that fits in 16 bits (verified exhaustively over all 2^21 inputs and against 200k randomized records vs RFC 1071 reference vectors). The same fix is applied to the L3 checksum path, where it additionally repairs the truncated short-record path (idempotent for full records). Adds a cocotb regression to the tx_deparse testbench driving maximal checksum inputs on TCP/UDP over IPv4/IPv6; it fails on the previous behaviour (off-by-one) and passes with this change. The pre-existing tests are unchanged and still pass.
Author
|
/gha run Regression Tests |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
zircon_ip_tx_deparseaccumulates the L4 checksum into a 21-bit register(
meta_l4_cksum_reg). With maximal header field values (all-ones IPv4/IPv6addresses, ports, TCP sequence/acknowledge numbers, window/urgent pointer —
all software-supplied TX metadata) the accumulated sum reaches ~0x9510F, far
beyond 17 bits.
At the start of packet generation the accumulator was folded back ONCE:
and the checksum is truncated to 16 bits on output (TCP
:877, UDP:902).The single fold can itself produce a carry (e.g. S = 0x8FFFE -> T = 0x10006),
and that carry is silently dropped by the truncation, so the transmitted
checksum is wrong by exactly the dropped carry (off by one in every reachable
case).
Measured (all-ones TCP/IPv6 metadata, 2-byte payload 0xAEEB): sum S = 0x8FFFE,
DUT transmits checksum 0xFFF9 while the correct value is 0xFFF8. UDP/IPv6 with
payload 0xFFDB: S = 0x2FFFE, DUT transmits 0xFFFF, correct is 0xFFFE. The same
values reproduce for TCP/IPv4 and UDP/IPv4 (identical accumulated sums).
The existing tests never push the accumulator past 0x10000, so this was not
caught by CI. For dense/jumbo traffic the failure region is hit roughly once
per ~14k packets (silent, receiver drops the segment); flows with maximal
metadata field values can hit it deterministically.
The IPv4 header checksum capture had the same shape for its own accumulator
(
pkt_l3_cksum_next = ~meta_l3_cksum_regwith a 21-bit register, truncated to16 bits at output): fully-ingested records already fold it twice during
metadata ingest, but the fold was not repeated at capture time.
Change
Fold the end-around carry TWICE before complementing, for both the L3 and the
L4 checksums:
Two folds always suffice: the first fold of a 21-bit sum is at most
0x1001E, so the second fold can never carry again. The double fold is
idempotent for values that are already fully folded (the L3 accumulator after
complete 16-word ingest), so behavior for existing traffic is unchanged.
Test
Added
run_test_l4_cksum_endaround_carrytotb/zircon_ip_tx_deparse: drivesTCP and UDP over IPv4 and IPv6 with maximal header field values and 2-byte
payloads chosen so the accumulated sum sits exactly at the carry-fold boundary
(S[15:0] = 0xFFFE with S >> 16 >= 1). Asserts the emitted checksum matches
the scapy reference packet. This test fails on the unfixed RTL (TCP checksum
0xFFF9 vs 0xFFF8) and passes with the fix; the pre-existing tests are
unchanged and still pass.